package com.tianya.common.redis.lock;

import com.tianya.common.constant.CommonConstant;
import com.tianya.common.exception.LockException;
import com.tianya.common.lock.DistributedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import java.util.concurrent.TimeUnit;

@ConditionalOnClass(RedissonClient.class)
@ConditionalOnProperty(prefix = "tianya.lock", name = "lockType", havingValue = "REDIS", matchIfMissing = true)
public class RedissionDistributedLock implements DistributedLock {

    @Autowired
    private RedissonClient redisson;

    private RLock getLock(String key, boolean isFair){
        if (isFair) {
            return redisson.getFairLock(CommonConstant.LOCK_KEY_PREFIX + key);
        }
        return redisson.getLock(CommonConstant.LOCK_KEY_PREFIX + key);
    }

    @Override
    public Object lock(String key, long leaseTime, TimeUnit unit, boolean isFair) throws Exception {
        RLock lock = getLock(key, isFair);
        lock.lock(leaseTime, unit);
        return lock;
    }

    @Override
    public Object lock(String key, long leaseTime, TimeUnit unit) throws Exception {
        return lock(key, leaseTime, unit, false);
    }

    @Override
    public Object lock(String key, boolean isFair) throws Exception {
        return lock(key, -1, null, false);
    }

    @Override
    public Object lock(String key) throws Exception {
        return lock(key, -1, null, false);
    }

    @Override
    public Object tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, boolean isFair) throws Exception {
        RLock lock = getLock(key, isFair);
        if (lock.tryLock( waitTime, leaseTime, unit)){
            return lock;
        }
        return null;
    }

    @Override
    public Object tryLock(String key, long waitTime, long leaseTime, TimeUnit unit) throws Exception {
        return tryLock(key, waitTime, leaseTime, unit, false);
    }

    @Override
    public Object tryLock(String key, long waitTime, TimeUnit unit, boolean isFair) throws Exception {
        return tryLock(key, waitTime, -1, unit, isFair);
    }

    @Override
    public Object tryLock(String key, long waitTime, TimeUnit unit) throws Exception {
        return tryLock(key, waitTime, -1,unit, false);
    }

    @Override
    public void unLock(Object lock) throws Exception {
        if(lock != null) {
            if (lock instanceof RLock){
                RLock rLock = (RLock) lock;
                if (rLock.isLocked()){
                    rLock.unlock();
                }
            } else {
                throw new LockException("requires RLock type can unlock");
            }
        }
    }
}
